#include "server.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "common.h"
#include <pthread.h>
#include "list.h"

#ifdef __linux__
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h>
#else
#include <Windows.h>
#include <WinSock2.h>
#endif
#include "list.h"
#include "log.h"

//#define MEMORY_DEBUG

void * my_malloc(size_t size)
{
#ifdef MEMORY_DEBUG
    static uint32_t cnt = 0;
    cnt++;
    log_info("malloc cnt: %d\n", cnt);
#endif
    return malloc(size);
}

void my_free(void *data)
{
#ifdef MEMORY_DEBUG
    static uint32_t cnt = 0;
    cnt++;
    log_info("free cnt: %d\n", cnt);
    fflush(stdout);
#endif
    free(data);
}

typedef struct tcp_recv_buf {
    char *buf;
    size_t size;
} tcp_recv_buf_t;

typedef struct msg_to_do {
    tcp_recv_buf_t recv_buf;
    int sockfd;
    int connfd;
    pthread_t *msg_to_do_tid;
} msg_to_do_t;

typedef struct socket_server {
    char *sockfile;
    int sockfd;
    bool run;
    pthread_t tid;
    msg_handle msg_to_do;
} socket_server_t;

static socket_server_t server;

int socket_server_loop(char *sockfile);
static void * msg_to_do_thread_handler(void *ptr);

int socket_server_start(char *sockfile, msg_handle register_msg_handle)
{
    int ret;

    if (register_msg_handle == NULL) {
        return RET_NULL_PTR;
    }
    if (server.run) {
        return RET_RUNNING;
    }
    server.msg_to_do = register_msg_handle;

    ret = pthread_create(&server.tid, NULL, (void *)socket_server_loop, sockfile);
    if (ret) {
        return ret;
    }

    return RET_OK;
}

static void thread_exit_handler(void *arg)
{
    (void )arg;
    log_info("thread exit, thread tid: %ld\n", pthread_self());
}

int socket_server_loop(char *sockfile)
{
    pthread_cleanup_push(thread_exit_handler, NULL);
    if (sockfile == NULL) {
        return RET_NULL_PTR;
    }

    remove(sockfile);

    int ret;
    struct sockaddr_un unix_sock;
    uint32_t sockaddr_len = sizeof(unix_sock);

    server.sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (server.sockfd < 0) {
        return RET_SOCK_FAIL;
    }

    unix_sock.sun_family = AF_UNIX;
    strcpy(unix_sock.sun_path, sockfile);
    unlink(unix_sock.sun_path);

    int len = strlen(unix_sock.sun_path) + sizeof(unix_sock.sun_family);
    ret = bind(server.sockfd, (struct sockaddr *)&unix_sock, len);
    if (ret) {
        return ret;
    }

    ret = listen(server.sockfd, 5);
    if (ret) {
        return ret;
    }

    server.run = true;

    for (;;) {
        log_info("Wait for a connection...\n");
        int connfd;

        connfd = accept(server.sockfd, (struct sockaddr *)&unix_sock, &sockaddr_len);
        if (connfd < 0) {
            perror("accept error");
            continue;
        }

        msg_to_do_t *mtd = my_malloc(sizeof(msg_to_do_t));
        memset(mtd, 0, sizeof(msg_to_do_t));
        mtd->sockfd = server.sockfd;
        mtd->connfd = connfd;

        // todo: create thread to handle client request
        pthread_t *msg_to_do_tid = my_malloc(sizeof(pthread_t));
        mtd->msg_to_do_tid = msg_to_do_tid;

        ret = pthread_create(msg_to_do_tid, NULL, msg_to_do_thread_handler, mtd);
        if (ret) {
            perror("create thread error");
            my_free(mtd);
            my_free(msg_to_do_tid);
            continue;
        }

        pthread_detach(*msg_to_do_tid);
    }
    pthread_cleanup_pop(0);
}

static void msg_to_do_thread_exit_handler(void *ptr)
{
    msg_to_do_t *mtd = (msg_to_do_t *)ptr;
    my_free(mtd->msg_to_do_tid);
    my_free(mtd);
    log_info("thread exit clean up done\n");
}

/*
 * 注意:Linux默认的线程栈大小为8M. buf设置过大将导致栈溢出发生段错误
 */
static void * msg_to_do_thread_handler(void *ptr)
{
    log_info("msg to do thread start........\n");
    pthread_cleanup_push(msg_to_do_thread_exit_handler, ptr);
    msg_to_do_t *mtd = (msg_to_do_t *)ptr;
    char recv_buf[RECV_BUF_SIZE] = {(uint8_t)0};
    char out_buf[RECV_BUF_SIZE] = {(uint8_t )0};
    int len = 0;

    while (1) {
        mtd->recv_buf.size = recv(mtd->connfd, recv_buf, RECV_BUF_SIZE, 0);
        if (mtd->recv_buf.size <= 0) {
            log_info("client disconnect\n");
            pthread_exit(0);
        }

        mtd->recv_buf.buf = my_malloc(mtd->recv_buf.size + 1);
        memcpy(mtd->recv_buf.buf, recv_buf, mtd->recv_buf.size);

        len = server.msg_to_do(mtd->recv_buf.buf, out_buf, RECV_BUF_SIZE);
        if (len <= 0) {
            log_error("error: msg_to_do\rn");
            continue;
        }

        my_free(mtd->recv_buf.buf);
        mtd->recv_buf.buf = NULL;

        send(mtd->connfd, out_buf, len, 0);
    }

    pthread_cleanup_pop(1);

    log_info("--------------> this is a test for pthread_cleanup \n");

    return NULL;
}

void socket_server_stop()
{
    pthread_cancel(server.tid);
    pthread_join(server.tid, NULL);
    server.run = false;
}
